﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;

using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.InteropServices;


/// <summary>
/// Define many  utility functions used by other place  
/// </summary>
public class Utils
{
    public class ServiceInfo
    {
        private string ip;
        private int port;
        private int totalpackets;
        private int totalclients;
        private uint totalbytes;
        private string protocol;
        private string clientip;

        public string Ip
        {
            get { return ip; }
            set { ip = value; }
        }
        public int Port
        {
            get { return port; }
            set { port = value; }
        }
        public string Protocol
        {
            get { return protocol; }
            set { protocol = value; }
        }
        public int Totalpackets
        {
            get { return totalpackets; }
            set { totalpackets = value; }
        }
        public uint Totalbytes
        {
            get { return totalbytes; }
            set { totalbytes = value; }
        }
        public int Totalclients
        {
            get { return totalclients; }
            set { totalclients = value; }
        }
        public string Clientip
        {
            get { return clientip; }
            set { clientip = value; }
        }
        public override int GetHashCode()
        {
            string s = ip+"-"+port;
            return s.GetHashCode();
        }
    }

    public class IpPairFlow
    {
        private string srcip;
        private string dstip;
        private int inpackets;
        private int outpackets;
        private uint inbytes;
        private uint outbytes;
        private uint totalbytes;
        public string Srcip
        {
          get { return srcip; }
          set { srcip = value; }
        }
        public string Dstip
        {
          get { return dstip; }
          set { dstip = value; }
        }
        public int Inpackets
        {
            get { return inpackets; }
            set { inpackets = value; }
        }
        public int Outpackets
        {
            get { return outpackets; }
            set { outpackets = value; }
        }
        public uint Inbytes
        {
            get { return inbytes; }
            set { inbytes = value; }
        }
        public uint Outbytes
        {
            get { return outbytes; }
            set { outbytes = value; }
        }
        public uint Totalbytes
        {
          get { return totalbytes; }
          set { totalbytes = value; }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct Mypacket
    {
        public uint num;
        public uint length;
        public uint ipversion;
        public double capturetime;
        public uint type;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 72)]
        public string srcip;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 72)]
        public string dstip;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
        public string srcmac;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
        public string dstmac;

        public uint srcport;
        public uint dstport;
        public uint seqnum;
        public uint acknum;

        public byte URG;
        public byte ACK;
        public byte PSH;
        public byte RST;
        public byte SYN;
        public byte FIN;
    }
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool ParseFunc(string filepath, out IntPtr arrayPtr, out int size, out int total_size);
    /*
    [DllImport(@"D:\MyWebSites\PacketStatistic\App_Code\parse_file_dll.dll"))]
    public static extern bool Parse_pcap(string filepath, out IntPtr arrayPtr, out int size, out int total_size);*/

    static void InsertPacketInfo(Mypacket p, int fileid, SqlConnection conn)
    {
        string sql = "insert into packetinfo ( packetid, length, ipversion, srcip, dstip, srcmac, dstmac, srcport, dstport,capturetime, seqnum, acknum, ack, urg, psh, rst, syn, fin, fileid) ";
        sql += "values(@packetid,@length,@ipversion,@srcip,@dstip,@srcmac,@dstmac,@srcport,@dstport,@capturetime,@seqnum,@acknum,@ack,@urg,@psh,@rst,@syn,@fin,@fileid)";
        SqlParameter[] sqlParams = new SqlParameter[] { 
                    new SqlParameter("@packetid", SqlDbType.BigInt),
                    new SqlParameter("@length", SqlDbType.Decimal),
                    new SqlParameter("@ipversion", SqlDbType.Int),
                    new SqlParameter("@srcip", SqlDbType.Char),
                    new SqlParameter("@dstip", SqlDbType.Char),
                    new SqlParameter("@srcmac", SqlDbType.Char),
                    new SqlParameter("@dstmac", SqlDbType.Char), 
                    new SqlParameter("@srcport", SqlDbType.Int), 
                    new SqlParameter("@dstport", SqlDbType.Int),
                    new SqlParameter("@seqnum", SqlDbType.Decimal),
                    new SqlParameter("@acknum", SqlDbType.Decimal),
                    new SqlParameter("@ack", SqlDbType.Bit),
                    new SqlParameter("@urg", SqlDbType.Bit),
                    new SqlParameter("@psh", SqlDbType.Bit),
                    new SqlParameter("@rst", SqlDbType.Bit),
                    new SqlParameter("@syn", SqlDbType.Bit),
                    new SqlParameter("@fin", SqlDbType.Bit),
                    new SqlParameter("@fileid", SqlDbType.Int),
                    new SqlParameter("@capturetime", SqlDbType.Float),
                };
        sqlParams[0].Value = p.num;
        sqlParams[1].Value = p.length;
        sqlParams[2].Value = p.ipversion;
        sqlParams[3].Value = p.srcip;
        sqlParams[4].Value = p.dstip;
        sqlParams[5].Value = p.srcmac;
        sqlParams[6].Value = p.dstmac;
        sqlParams[7].Value = p.srcport;
        sqlParams[8].Value = p.dstport;
        sqlParams[9].Value = p.seqnum;
        sqlParams[10].Value = p.acknum;
        sqlParams[11].Value = p.ACK;
        sqlParams[12].Value = p.URG;
        sqlParams[13].Value = p.PSH;
        sqlParams[14].Value = p.RST;
        sqlParams[15].Value = p.SYN;
        sqlParams[16].Value = p.FIN;
        sqlParams[17].Value = fileid;
        sqlParams[18].Value = p.capturetime;

        SqlCommand cmd = new SqlCommand();
        sqlHelp.PrepareCommand(cmd, conn, null, CommandType.Text, sql, sqlParams);
        cmd.ExecuteNonQuery();
    }

    static void InsertFlowInfo(double timestamp,int flowrate , int fileid, SqlConnection conn )
    {
        string sql = "insert into flowinfo (timestamp, flowrate,fileid) ";
        sql += "values(@timestamp, @flowrate, @fileid)";
        SqlParameter[] sqlParams = new SqlParameter[] { 
                    new SqlParameter("@timestamp", SqlDbType.Float),
                    new SqlParameter("@flowrate", SqlDbType.Int),
                    new SqlParameter("@fileid", SqlDbType.Int),
         };
        sqlParams[0].Value = timestamp;
        sqlParams[1].Value = flowrate;
        sqlParams[2].Value = fileid;

        SqlCommand cmd = new SqlCommand();
        sqlHelp.PrepareCommand(cmd, conn, null, CommandType.Text, sql, sqlParams);
        cmd.ExecuteNonQuery();
    }

    public static bool ParseFile(string filepath,string filename, int filesize, string md5)
    {
        var arrayValue = IntPtr.Zero;
        var size = 0;
        var total_size = 0;
        //get the dll importing function address 
　　    DllInvoke dll = new DllInvoke(HttpContext.Current.Server.MapPath("App_Code/parse_file_dll.dll")); 
　　    ParseFunc Parse_pcap = (ParseFunc)dll.Invoke("Parse_pcap",typeof(ParseFunc)); 

        if (!Parse_pcap(filepath, out arrayValue, out size, out total_size))
        {
            return false;
        }
        if (size <= 0)
            return false;

        //Firstly insert pcap file information into the database
        InsertFileInfo(filename, filesize, md5);
        int fileid = GetFileId(md5); //get fileid from the database

        var structSize = Marshal.SizeOf(typeof(Mypacket));
        uint cur_total_bytes = 0;
        double prev_time = 0;
        int speed = 0;
        int i = 0;

        DataTable dtPacketInfo = new DataTable();
        DataTable dtFlowInfo = new DataTable();
        string[] attrs = {"packetid", "length", "ipversion", "srcip", "dstip", "srcmac", "dstmac",
        "srcport", "dstport","capturetime", "seqnum", "acknum", "ack", "urg", "psh", "rst", "syn", "fin", "fileid"};
        for (i = 0; i < attrs.Length; i++)
            dtPacketInfo.Columns.Add(attrs[i]);
        string[] attrs2 = { "timestamp", "flowrate", "fileid" };
        for (i = 0; i < attrs2.Length; i++)
            dtFlowInfo.Columns.Add(attrs2[i]);

        for (i = 0; i < size; i++)
        {
            var cur = (Mypacket)Marshal.PtrToStructure(arrayValue, typeof(Mypacket));
            cur_total_bytes += cur.length;
            double interval = cur.capturetime - prev_time;
            if (interval > 1.0)
            {
                speed = Convert.ToInt32((cur_total_bytes >> 7) / interval); //kb/s  note:cur_total_bytes/1024*8=cur_total_bytes >> 7
                DataRow flowrow = dtFlowInfo.NewRow();
                flowrow["timestamp"] = cur.capturetime;
                flowrow["flowrate"] = speed;
                flowrow["fileid"] = fileid;
                dtFlowInfo.Rows.Add(flowrow);
                //InsertFlowInfo(cur.capturetime, speed, fileid, conn); //insert flowinfo table
                prev_time = cur.capturetime;
                cur_total_bytes = 0;
            }
            DataRow dr = dtPacketInfo.NewRow();
            dr["packetid"] = cur.num;
            dr["length"] = cur.length;
            dr["ipversion"] = cur.ipversion;
            dr["srcip"] = cur.srcip;
            dr["dstip"] = cur.dstip;
            dr["srcmac"] = cur.srcmac;
            dr["dstmac"] = cur.dstmac;
            dr["srcport"] = cur.srcport;
            dr["dstport"] = cur.dstport;
            dr["capturetime"] = cur.capturetime;
            dr["seqnum"] = cur.seqnum;
            dr["acknum"] = cur.acknum;
            dr["ack"] = Convert.ToBoolean(cur.ACK);
            dr["urg"] = Convert.ToBoolean(cur.URG);
            dr["psh"] = Convert.ToBoolean(cur.PSH);
            dr["rst"] = Convert.ToBoolean(cur.RST);
            dr["syn"] = Convert.ToBoolean(cur.SYN);
            dr["fin"] = Convert.ToBoolean(cur.FIN);
            dr["fileid"] = fileid;
            dtPacketInfo.Rows.Add(dr);
               //InsertPacketInfo(cur, fileid, conn); //insert packetinfo table
            arrayValue = new IntPtr(arrayValue.ToInt32() + structSize);
        }
        using (SqlBulkCopy sbc = new SqlBulkCopy(sqlHelp.ConnectionStringLocalTransaction))
        {
            sbc.DestinationTableName = "packetinfo";
            for (i = 0; i < attrs.Length; i++)
                sbc.ColumnMappings.Add(attrs[i], attrs[i]);
            sbc.WriteToServer(dtPacketInfo);  
        }
        using (SqlBulkCopy sbc = new SqlBulkCopy(sqlHelp.ConnectionStringLocalTransaction))
        {
            sbc.DestinationTableName = "flowinfo";
            for (i = 0; i < attrs2.Length; i++)
                sbc.ColumnMappings.Add(attrs2[i], attrs2[i]);
            sbc.WriteToServer(dtFlowInfo);
        }       
        return true;
    }

    public static List<ServiceInfo> GetAllService(int fileid)
    {
        uint seqnum, acknum;
        List<ServiceInfo> serviceInfo = new List<ServiceInfo>();
        //HashSet<Service> hs = new HashSet<Service>();
        Dictionary<string,int> packetCount = new Dictionary<string,int>();
        Dictionary<string, uint> byteCount = new Dictionary<string, uint>();
        Dictionary<string,HashSet<String>> clientCount = new Dictionary<string,HashSet<string>>();
        List<UInt32> tcplist = new List<UInt32>();
        bool syn;
        bool ack;
        string srcip;
        string dstip;
        int srcport;
        int dstport;
        uint length;
        string str1, str2;
        string sql = "select * from packetinfo where fileid="+fileid+"and (syn=1 or ack=1)";
        string[] portmap = new string[1023];
        string line;
        int i;
        StreamReader file = new StreamReader(HttpContext.Current.Server.MapPath("App_Code/ports.txt"));
        while ((line = file.ReadLine()) != null) //just consider the port < 1024
        {
            string[] s = line.Split();
            i = Convert.ToInt32(s[1]);
            if (i < 1024)
                portmap[i] = s[0];
        }
        file.Close();
        using (SqlConnection conn = new SqlConnection(sqlHelp.ConnectionStringLocalTransaction))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            SqlDataReader dr = cmd.ExecuteReader();
            while (dr.Read())
            {
                syn = dr.GetBoolean(16);
                ack = dr.GetBoolean(13);
                srcip = dr["srcip"].ToString().TrimEnd();
                dstip = dr["dstip"].ToString().TrimEnd();
                srcport = (int)dr["srcport"];
                dstport = (int)dr["dstport"];
                length = (uint)(decimal)dr["length"];
                str1 = srcip+"-"+srcport;
                str2 = dstip + "-" + dstport;
                if (packetCount.ContainsKey(str1))
                {
                    packetCount[str1] += 1;
                    byteCount[str1] += length;
                }
                if (packetCount.ContainsKey(str2))
                {
                    packetCount[str2] += 1;
                    byteCount[str2] += length;
                }
                if (clientCount.ContainsKey(str1))
                    clientCount[str1].Add(str2);
                if (syn && !ack) //first tcp handshake
                {
                    seqnum = (uint)dr.GetDecimal(10);
                    tcplist.Add(seqnum);
                }
                else if (syn && ack) //second tcp handshake
                {
                    seqnum = (uint)dr.GetDecimal(10);
                    acknum = (uint)dr.GetDecimal(11);
                    int index = tcplist.LastIndexOf(acknum-1);
                    if( index != -1)
                    {
                        tcplist.RemoveAt(index);
                        tcplist.Add(seqnum);
                    }
                }
                else if(!syn && ack) //third tcp handshake
                {
                    acknum = (uint)dr.GetDecimal(11);
                    if (tcplist.LastIndexOf(acknum - 1) != -1)
                    {
                        ServiceInfo s = new ServiceInfo();
                        s.Ip = dstip;
                        s.Port = dstport;
                        /*
                        bool existed = false;
                        for (int i = 0; i < serviceInfo.Count; i++)
                            if (serviceInfo[i].ip.Equals(s.ip) && serviceInfo[i].port == s.port)
                            {
                                existed = true;
                                break;
                            }
                        if (existed)
                            continue;*/
                        s.Totalclients = 0;
                        s.Totalpackets = 0;
                        //if(hs.Add(s))
                        if (!packetCount.ContainsKey(str2)) //add if this service does not exist
                        {
                            packetCount.Add(str2, 0);
                            byteCount.Add(str2, 0);
                            clientCount.Add(str2, new HashSet<string>());
                            serviceInfo.Add(s);
                        }
                    }
                }
                
            }
            dr.Close();
            conn.Close();      
        }
        for (i = 0; i < serviceInfo.Count; i++)
        {
            str1 = serviceInfo[i].Ip+"-"+serviceInfo[i].Port;
            serviceInfo[i].Totalpackets = packetCount[str1];
            serviceInfo[i].Totalclients = clientCount[str1].Count;
            serviceInfo[i].Totalbytes = byteCount[str1];
            serviceInfo[i].Clientip = clientCount[str1].ElementAt(0).Split('-')[0];
            int port = serviceInfo[i].Port;
            if (port < 1024)
                serviceInfo[i].Protocol = portmap[port];
        }
        return serviceInfo;
    }
    public static List<IpPairFlow> GetIpPairFlow(int fileid)
    {
        string sql = "select srcip, dstip, count(*) as packets,sum(length) as bytes from packetinfo where fileid=" + fileid + " group by srcip,dstip";
        string srcip, dstip;
        uint  bytes;
        int packets;
        string key1, key2;
        List<IpPairFlow> ipflow = new List<IpPairFlow>();
        Dictionary<string, IpPairFlow> flow = new Dictionary<string, IpPairFlow>();
        int count1 = 0;
        int count2 = 0;
        int count3 = 0;
        DataSet dataset = new DataSet();
        using (SqlConnection conn = new SqlConnection(sqlHelp.ConnectionStringLocalTransaction))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn); 
            /*
            SqlDataReader dr = cmd.ExecuteReader();
            while (dr.Read())
            {
                srcip = dr["srcip"].ToString().TrimEnd();
                dstip = dr["dstip"].ToString().TrimEnd();
                bytes = (uint)(decimal)dr["bytes"];
                packets = (int)dr["packets"];
                key1 = srcip + '-' + dstip;
                key2 = dstip + '-' + srcip;
                if (!flow.ContainsKey(key1) && !flow.ContainsKey(key2))
                {
                    count1 += 1;
                    IpPairFlow info = new IpPairFlow();
                    info.Srcip = srcip;
                    info.Dstip = dstip;
                    info.Outbytes = bytes;
                    info.Outpackets = packets;
                    flow[key1] = info;
                }
                else
                {
                    if (flow.ContainsKey(key2))
                    {
                        count2 += 1;
                        flow[key2].Inbytes = bytes;
                        flow[key2].Inpackets = packets;  
                    }
                    if (flow.ContainsKey(key1))
                        count3 += 1;
                }   
            }
            dr.Close();*/
            SqlDataAdapter da = new SqlDataAdapter(sql, conn);
            da.Fill(dataset);   
            conn.Close();
        }
        foreach (DataRow dr in dataset.Tables[0].Rows)
        {
            srcip = dr["srcip"].ToString().TrimEnd();
            dstip = dr["dstip"].ToString().TrimEnd();
            bytes = (uint)(decimal)dr["bytes"];
            packets = (int)dr["packets"];
            key1 = srcip + '-' + dstip;
            key2 = dstip + '-' + srcip;
            if (!flow.ContainsKey(key1) && !flow.ContainsKey(key2))
            {
                count1 += 1;
                IpPairFlow info = new IpPairFlow();
                info.Srcip = srcip;
                info.Dstip = dstip;
                info.Outbytes = bytes;
                info.Outpackets = packets;
                flow[key1] = info;
            }
            else
            {
                if (flow.ContainsKey(key2))
                {
                    count2 += 1;
                    flow[key2].Inbytes = bytes;
                    flow[key2].Inpackets = packets;
                }
                if (flow.ContainsKey(key1))
                    count3 += 1;
            }   
        }
        foreach (IpPairFlow item in flow.Values)
        {
            item.Totalbytes = item.Inbytes + item.Outbytes;
            ipflow.Add(item);
        }
        flow.Clear();
        return ipflow;
    }

    public static void DeleteFileData(string md5)
    {
        int fileid =  GetFileId(md5);
        if (fileid == -1)
            return;
        string sql1 = "delete from  packetinfo where (fileid=" + fileid+ ")";
        string sql2 = "delete from  flowinfo where (fileid=" + fileid + ")";
        string sql3 = "delete from  fileinfo where (fileid=" + fileid + ")";
        using (SqlConnection conn = new SqlConnection(sqlHelp.ConnectionStringLocalTransaction))
        {
            conn.Open();
            SqlCommand cmd1 = new SqlCommand(sql1, conn);
            SqlCommand cmd2 = new SqlCommand(sql2, conn);
            SqlCommand cmd3 = new SqlCommand(sql3, conn);
            int a= cmd1.ExecuteNonQuery(); 
            a = cmd2.ExecuteNonQuery();
            a = cmd3.ExecuteNonQuery();
            conn.Close();
        }
    }

    public static bool IsFileExisted(string md5)
    {
        int count = 0;
        string sql = "select count(*) from  fileinfo where (md5='"+md5+"')";
        using (SqlConnection conn = new SqlConnection(sqlHelp.ConnectionStringLocalTransaction))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            count = (int)cmd.ExecuteScalar();
            conn.Close();
        }
       
        if (count >= 1)
            return true;
        return false;
    }

    public static int GetFileId(string md5)
    {
        string sql = "select * from  fileinfo where (md5='" + md5 + "')";
        SqlDataReader sdr;
        int fileid = -1;
        using (SqlConnection conn = new SqlConnection(sqlHelp.ConnectionStringLocalTransaction))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            sdr = cmd.ExecuteReader(CommandBehavior.SingleResult);
            if (sdr.Read())
            {
                fileid = sdr.GetInt32(0);
                //int i = sdr.GetOrdinal("filename");
                //int j = sdr.GetOrdinal("filesize");
                //int k = sdr.GetOrdinal("md5");
                //string filename = sdr.GetString(i);
                //int filesize = sdr.GetInt32(j);
                //string hash = sdr.GetString(k);
            }
            sdr.Close();
            conn.Close();
            
        }
        return fileid;
    }

    public static void InsertFileInfo(string filename, int filesize,string md5)
    {
        //insert to table fileinfo 
        string sql = "insert into fileinfo ( filename, filesize, md5) ";
        sql += "values(@filename, @filesize, @md5)";
        SqlParameter[] sqlParams = new SqlParameter[] { 
        new SqlParameter("@filename", SqlDbType.VarChar),
        new SqlParameter("@filesize", SqlDbType.Int),
        new SqlParameter("@md5", SqlDbType.Char)
        };
   
        sqlParams[0].Value = filename;
        sqlParams[1].Value = filesize;
        sqlParams[2].Value = md5;
      
        sqlHelp.ExecuteNonQuery(sqlHelp.ConnectionStringLocalTransaction, CommandType.Text, sql, sqlParams);
    }

    public static string GetHash(string filepath)
    {
        String md5Result;
        StringBuilder sb = new StringBuilder();
        MD5 md5Hasher = MD5.Create();

        using (FileStream fs = File.OpenRead(filepath))
        {
            foreach (Byte b in md5Hasher.ComputeHash(fs))
                sb.Append(b.ToString("x2").ToLower());
        }

        md5Result = sb.ToString();

        return md5Result;
    }
}

/// <summary>
/// The class for loading our own dll
/// </summary>
public class DllInvoke
{
    [DllImport("kernel32.dll")]
    private extern static IntPtr LoadLibrary(String path);
    [DllImport("kernel32.dll")]
    private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
    [DllImport("kernel32.dll")]
    private extern static bool FreeLibrary(IntPtr lib);
    private IntPtr hLib;
    public DllInvoke(String DLLPath)
    {
        hLib = LoadLibrary(DLLPath);
    }
    ~DllInvoke()
    {
        FreeLibrary(hLib);
    }
    public Delegate Invoke(String APIName, Type t)
    {
        IntPtr api = GetProcAddress(hLib, APIName);
        return (Delegate)Marshal.GetDelegateForFunctionPointer(api, t);
    }
}